package com.zhilei.exceptions;

import com.zhilei.grace.result.GraceJSONResult;
import com.zhilei.grace.result.ResponseStatusEnum;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.UnsupportedJwtException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.security.SignatureException;
import java.util.HashMap;
import java.util.List;

/**
 * 这会标注成为全局的异常通知类，来全局处理所有控制器的异常，用于对Controller进行切面环绕的
 * 作用是给Controller控制器添加统一的操作或处理。
 * ControllerAdvice拆开来就是Controller Advice，关于Advice，在Spring的AOP中，
 * 是用来封装一个切面所有属性的，包括切入点和需要织入的切面逻辑
 *
 * @author 16833
 */
@ControllerAdvice  // 使这个类成为全局的控制器通知类，用来处理所有控制器的异常
public class GraceExceptionHandler {

    /**
     * 我标注了这是一个全局异常处理类，指定了需要处理的全局异常类是MyExceptions
     * 这相当于AOP进行切面，切的是所有的controller
     * 当MyExceptions发生异常时，会进行全局捕获
     * 主要的逻辑是 tomcat-->过滤器-->servlet-->拦截器(会在拦截器这里做校验，是不是需要放行到controller，抛出异常就会全局进行捕获)--->controller
     *
     * @param e 指定捕获的异常
     * @return JSON数据给前端
     */
    @ResponseBody
    // 当被标注的方法捕获到指定类型的异常时，会执行该方法来处理异常
    @ExceptionHandler(MyExceptions.class)
    public GraceJSONResult handleMyExceptions(MyExceptions e) {
        // return GraceJSONResult.exception(ResponseStatusEnum.SMS_NEED_WAIT_ERROR);
        return GraceJSONResult.exception(e.getResponseStatusEnum());
    }

    /**
     * JWT的异常捕获
     *
     * @param e
     * @return
     */
    @ExceptionHandler({
            SignatureException.class,
            ExpiredJwtException.class,
            UnsupportedJwtException.class,
            MalformedJwtException.class,
            io.jsonwebtoken.security.SignatureException.class
    })
    @ResponseBody
    public GraceJSONResult returnSignatureException(SignatureException e) {
        return GraceJSONResult.exception(ResponseStatusEnum.JWT_SIGNATURE_ERROR);
    }

    /**
     * 处理参数不被校验的异常
     *
     * @param e
     * @return
     */
    @ResponseBody
    // 参数不被校验的异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public GraceJSONResult handleNotValidExceptions(MethodArgumentNotValidException e) {
        // BindingResult是Spring MVC中用于封装表单数据校验结果的对象
        // 当表单验证失败时会抛出MethodArgumentNotValidException，这里面会包含BindingResult对象
        // BindingResult对象里面包含了表单验证失败的详细信息
        BindingResult result = e.getBindingResult();
        HashMap<Object, Object> errors = getError(result);
        return GraceJSONResult.errorMap(errors);
    }

    /**
     * 封装表单验证失败的字段名称跟返回值
     *
     * @param result
     * @return
     */
    public HashMap<Object, Object> getError(BindingResult result) {

        HashMap<Object, Object> errorsMap = new HashMap<>();

        // 从BindingResult对象中获取FieldError列表，FieldError对象表示了校验失败的字段和错误信息。
        // 通过遍历FieldError列表，可以获取每个校验失败的字段以及对应的错误信息，进而进行相应的错误处理或提示给用户
        List<FieldError> fieldErrors = result.getFieldErrors();
        for (FieldError f : fieldErrors) {

            // 获取的属性的名称
            String field = f.getField();
            // 获取的异常地返回值
            String msg = f.getDefaultMessage();
            errorsMap.put(field, msg);

        }
        return errorsMap;

    }
}











